package org.example.jingbian_quanjiafu;

import okhttp3.*;
import org.apache.commons.codec.binary.Base64;
import org.apache.http.*;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.*;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.example.SalesClient;
import org.json.JSONArray;
import org.json.JSONObject;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.time.LocalDate;
import java.util.*;

public class JingBian_XiaoShou {
    private static final Logger logger = LoggerFactory.getLogger(JingBian_XiaoShou.class);
    private static final String LOGIN_URL = "https://www.hshscm.com/Default.aspx";
    private static final String USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36";

    private CloseableHttpClient client;
    private HttpClientContext context;
    private Map<String, String> params = new HashMap<>();
    private boolean isInitialized = false;

    /**
     * 执行数据抓取并上传操作
     *
     * @param date 日期对象
     * @return 执行成功返回true，失败返回false
     * @throws Exception 执行过程中出现异常
     */
    public boolean execute(LocalDate date) {
        try {
            // 登录成功判断
            if(initializeSession()){
                // 登录成功 查找数据
                String html = fetchData(date);
                // 判断是否抓取到数据
                if (html == null) {
                    logger.warn("获取" + date.toString() + "数据失败");
                    return false;
                }

                // 上传数据
                JSONArray data = dataUploader(html, date);
                // 判断是否获取到有效数据
                if (data != null && !data.isEmpty()) {
                    // 执行上传销售操作
                    return SalesClient.postRequest(data, "靖边全家福", date.toString());
                }
                logger.info("未获取到" + date.toString() + "有效数据");
                return false;
            }else {
                return false;
            }
        } catch (Exception e) {
            // 记录异常日志
            logger.error("执行过程中发生异常", e);
            // 重置会话
            resetSession();
            return false;
        }
    }


    /**
     * 登录
     *
     * @return 如果会话初始化成功，则返回true；否则返回false
     * @throws Exception 如果在初始化会话过程中发生异常，则抛出异常
     */
    private boolean initializeSession() throws Exception {
        // 重置会话
        resetSession();
        // 创建自定义的HttpClient
        client = HttpClients.custom()
                .setRedirectStrategy(new LaxRedirectStrategy())
                .build();

        // 创建HttpClientContext
        context = HttpClientContext.create();

        // 执行初始请求
        //executeInitialRequest();
        // 创建HttpGet请求
        HttpGet initRequest = new HttpGet(LOGIN_URL);
        // 设置请求头
        setHeaders(initRequest);

        // 执行请求并获取响应
        try (CloseableHttpResponse response = client.execute(initRequest, context)) {
            // 解析响应内容
            parsePageParams(EntityUtils.toString(response.getEntity()));
        }

        // 处理验证码
        processCaptcha();

        // 提交登录信息
        if (submitLogin()) {
            // 设置会话初始化状态为true
            isInitialized = true;
            // 记录登录成功日志
            logger.info("登录成功");
            return true;
        } else {
            // 设置会话初始化状态为false
            isInitialized = false;
            // 记录登录失败日志
            logger.info("登录失败");
            return false;

        }
    }

    /**
     * 重置会话。
     *
     * <p>此方法会关闭当前的HTTP客户端，并将相关变量重置为初始状态。
     *
     * @throws IOException 如果关闭HTTP客户端时发生错误
     */
    private void resetSession() {
        try {
            if (client != null) {
                client.close();
            }
        } catch (IOException e) {
            logger.error("关闭HTTP客户端失败", e);
        }
        client = null;
        context = null;
        isInitialized = false;
        params.clear();
    }

    /**
     * 执行初始请求
     *
     * @throws Exception 抛出异常
     */
    // 以下是原有方法改造
    private void executeInitialRequest() throws Exception {
        // 创建HttpGet请求
        HttpGet initRequest = new HttpGet(LOGIN_URL);
        // 设置请求头
        setHeaders(initRequest);

        // 执行请求并获取响应
        try (CloseableHttpResponse response = client.execute(initRequest, context)) {
            // 解析响应内容
            parsePageParams(EntityUtils.toString(response.getEntity()));
        }
    }


    /**
     * 解析页面参数
     *
     * @param html HTML页面内容
     */
    private void parsePageParams(String html) {
        Document doc = Jsoup.parse(html);
        params.put("__VIEWSTATE", doc.selectFirst("#__VIEWSTATE").val());
        params.put("__VIEWSTATEGENERATOR", doc.selectFirst("#__VIEWSTATEGENERATOR").val());
        params.put("__EVENTVALIDATION", doc.selectFirst("#__EVENTVALIDATION").val());
    }

    /**
     * 处理验证码
     *
     * @throws Exception 如果在处理验证码过程中发生异常，将抛出此异常
     */
    private void processCaptcha() throws Exception {
        // 生成一个随机的验证码密钥
        String randomK = String.valueOf((int)(Math.random() * 9000 + 1000));
        // 构造验证码图片的URL
        String captchaUrl = "https://www.hshscm.com/codejoy.aspx?k=" + randomK;

        // 创建HTTP GET请求对象
        HttpGet captchaRequest = new HttpGet(captchaUrl);
        // 设置请求头
        setHeaders(captchaRequest);

        // 执行HTTP GET请求并获取响应
        try (CloseableHttpResponse response = client.execute(captchaRequest, context)) {
            // 将响应中的图片数据转换为字节数组
            byte[] imageBytes = EntityUtils.toByteArray(response.getEntity());
            // 创建验证码服务实例
            CaptchaService captchaService = new CaptchaService();
            // 识别验证码图片中的文字
            String captchaCode = captchaService.recognizeCaptcha(imageBytes);
            // 将识别到的验证码文字存入参数中
            params.put("CAPTCHA_CODE", captchaCode);
            // 刷新页面参数
            refreshPageParams();
        }
    }


    /**
     * 刷新页面参数
     *
     * @throws Exception 如果刷新过程中出现异常，则抛出异常
     */
    private void refreshPageParams() throws Exception {
        // 创建一个HTTP GET请求
        HttpGet refreshRequest = new HttpGet(LOGIN_URL);
        // 设置请求头
        setHeaders(refreshRequest);

        // 执行HTTP请求并获取响应
        try (CloseableHttpResponse response = client.execute(refreshRequest, context)) {
            // 解析页面参数
            parsePageParams(EntityUtils.toString(response.getEntity()));
        }
    }


    private boolean submitLogin() throws Exception {
        HttpPost loginPost = new HttpPost(LOGIN_URL);
        setHeaders(loginPost);
        loginPost.setEntity(new UrlEncodedFormEntity(buildLoginFormParams(), "UTF-8"));

        try (CloseableHttpResponse response = client.execute(loginPost, context)) {
            String body = EntityUtils.toString(response.getEntity());
            if (body.contains("Main.aspx")) return true;
            if (body.contains("验证码错误")) logger.error("验证码识别错误");
            if (body.contains("用户名或密码错误")) logger.error("凭证错误");
            return false;
        }
    }

    /**
     * 构建登录表单参数列表
     *
     * @return 包含登录表单参数的列表
     */
    private List<NameValuePair> buildLoginFormParams() {
        return Arrays.asList(
                new BasicNameValuePair("__VIEWSTATE", params.get("__VIEWSTATE")),
                new BasicNameValuePair("__VIEWSTATEGENERATOR", params.get("__VIEWSTATEGENERATOR")),
                new BasicNameValuePair("__EVENTVALIDATION", params.get("__EVENTVALIDATION")),
                new BasicNameValuePair("UserLogin1$tbUserid", "sxxs10140"),
                new BasicNameValuePair("UserLogin1$tbPassword", "111111"),
                new BasicNameValuePair("UserLogin1$TextBox1", params.get("CAPTCHA_CODE")),
                new BasicNameValuePair("UserLogin1$Button1", "登录")
        );
    }

    /**
     * 从指定日期获取数据
     *
     * @param date 需要获取数据的日期
     * @return 获取到的数据，以字符串形式返回
     * @throws Exception 如果在获取数据过程中出现异常，将抛出异常
     */
    private String fetchData(LocalDate date) throws Exception {
        try {
            // 获取动态请求参数
            Map<String, String> formParams = getDynamicParams();

            // 创建HttpPost对象
            HttpPost dataPost = new HttpPost("https://www.hshscm.com/vendorsaledaysdatatime.aspx");

            // 设置请求头
            setHeaders(dataPost);

            // 设置请求体
            dataPost.setEntity(new UrlEncodedFormEntity(buildDataParams(formParams, date), "UTF-8"));

            // 执行HttpPost请求并获取响应
            try (CloseableHttpResponse response = client.execute(dataPost, context)) {
                // 将响应实体转换为字符串并返回
                return EntityUtils.toString(response.getEntity());
            }
        } catch (Exception e) {
            // 记录错误日志
            logger.error("数据获取失败", e);

            // 刷新页面参数
            refreshPageParams();

            // 抛出异常
            throw e;
        }
    }


    /**
     * 获取动态请求体
     *
     * @return 包含动态参数的Map集合
     * @throws Exception 当获取动态参数失败时抛出异常
     */
    private Map<String, String> getDynamicParams() throws Exception {
        HttpGet paramRequest = new HttpGet("https://www.hshscm.com/vendorsaledaysdatatime.aspx");
        setHeaders(paramRequest);

        try (CloseableHttpResponse response = client.execute(paramRequest, context)) {
            String html = EntityUtils.toString(response.getEntity());
            Document doc = Jsoup.parse(html);
            Map<String, String> dynamicParams = new HashMap<>();
            dynamicParams.put("__VIEWSTATE", doc.selectFirst("#__VIEWSTATE").val());
            dynamicParams.put("__VIEWSTATEGENERATOR", doc.selectFirst("#__VIEWSTATEGENERATOR").val());
            dynamicParams.put("__EVENTVALIDATION", doc.selectFirst("#__EVENTVALIDATION").val());
            return dynamicParams;
        }
    }

    /**
     * 获取数据的 请求体
     *
     * @param params 包含表单参数的Map，例如"__VIEWSTATE", "__VIEWSTATEGENERATOR"等
     * @param date 请求中需要包含的日期
     * @return 包含构建好的请求参数的列表
     */
    private List<NameValuePair> buildDataParams(Map<String, String> params, LocalDate date) {
        return Arrays.asList(
                new BasicNameValuePair("__VIEWSTATE", params.get("__VIEWSTATE")),
                new BasicNameValuePair("__VIEWSTATEGENERATOR", params.get("__VIEWSTATEGENERATOR")),
                new BasicNameValuePair("__EVENTVALIDATION", params.get("__EVENTVALIDATION")),
                new BasicNameValuePair("txtbegin", date.toString()),
                new BasicNameValuePair("txtEnd", date.toString()),
                new BasicNameValuePair("btnselect", "")
        );
    }

    /**
     * 将HTML页面中的表格数据转换为JSON数组
     *
     * @param html 包含表格数据的HTML字符串
     * @param date 销售日期
     * @return 包含表格数据的JSON数组
     */
    private JSONArray dataUploader(String html, LocalDate date) {
        // 解析HTML字符串为Document对象
        Document doc = Jsoup.parse(html);
        // 创建一个空的JSONArray对象用于存储表格数据
        JSONArray data = new JSONArray();

        // 遍历所有包含onmouseover属性的tr元素
        for (Element row : doc.select("tr[onmouseover]")) {
            // 获取当前行的所有td元素
            Elements cols = row.select("td");
            // 如果td元素数量少于9个，跳过当前行
            if (cols.size() < 9) continue;

            // 创建一个JSONObject对象用于存储当前行的数据
            JSONObject item = new JSONObject();
            item.put("条码", cols.get(1).text());
            item.put("名称", cols.get(2).text());
            item.put("单价", "");
            item.put("数量", cols.get(5).text());
            item.put("金额", cols.get(8).text());
            item.put("店名", "靖边全家福中心店");
            item.put("区域", "靖边");
            item.put("销售时间", date.toString() + " 00:00:00");
            data.put(item);
        }
        // 返回存储表格数据的JSONArray
        return data;
    }
    /**
     * 设置HTTP请求头
     *
     * @param request HttpRequest对象，用于设置HTTP请求头
     */
    private void setHeaders(HttpRequest request) {
        request.setHeader("User-Agent", USER_AGENT);
        request.setHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
        request.setHeader("Accept-Language", "zh-CN,zh;q=0.9");
        request.setHeader("Referer", LOGIN_URL);
    }

    //获取验证码类
    static class CaptchaService {
        private static final String API_URL = "http://api.jfbym.com/api/YmServer/customApi";
        private static final String API_TOKEN = "stKKX2fN1TaiulaMX554mFwi1p0cL0Gauw9wVzAo_0U";
        private final OkHttpClient client = new OkHttpClient();

        public String recognizeCaptcha(byte[] imageBytes) throws IOException {
            RequestBody body = new FormBody.Builder()
                    .add("image", Base64.encodeBase64String(imageBytes))
                    .add("token", API_TOKEN)
                    .add("type", "10110")
                    .build();

            Request request = new Request.Builder()
                    .url(API_URL)
                    .post(body)
                    .build();

            try (Response response = client.newCall(request).execute()) {
                String json = response.body().string();
                if (json.contains("\"code\":10000")) {
                    return json.split("\"data\":\"")[1].split("\"")[0];
                }
                throw new IOException("验证码识别失败：" + json);
            }
        }
    }
}